using System;
using System.Collections.Generic;
using System.IO.Packaging;
using System.IO;
using System.Xml;
using System.Diagnostics;

namespace BIUtilities.Publish
{
    /// <summary>
    /// Package Management for Office Packages (docx)
    /// </summary>
    public class PackageManager : IDisposable
    {
        const string RELATIONSHIP_OFFICEDOCUMENT = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument";
        const string RELATIONSHIP_CONNECTIONS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/connections";

        Package _package = null;
        PackagePart _primaryDocumentPart = null;

        /// <summary>
        /// Setup a package manager to handle the package modifications
        /// </summary>
        /// <param name="fileName"></param>
        public PackageManager(string fileName)
        {
            _package = Package.Open(fileName, FileMode.Open, FileAccess.ReadWrite);
            _primaryDocumentPart = GetPrimaryDocumentPart();
        }

        /// <summary>
        /// Return the first found / primary document part
        /// </summary>
        /// <returns></returns>
        private PackagePart GetPrimaryDocumentPart()
        {
            //  Get the main document part (workbook.xml, document.xml, presentation.xml).
            foreach (PackageRelationship relationship in _package.GetRelationshipsByType(RELATIONSHIP_OFFICEDOCUMENT))
            {
                //  There should only be one document part in the package. 
                Uri documentUri = PackUriHelper.ResolvePartUri(new Uri("/", UriKind.Relative), relationship.TargetUri);
                return _package.GetPart(documentUri);
            }

            throw new Exception("Office Document relationship is not found in package.");
        }

        /// <summary>
        /// Get the Connection Part list of package parts
        /// </summary>
        /// <returns></returns>
        private List<PackagePart> GetConnectionXmlPartList()
        {
            List<PackagePart> foundCustomXmlParts = new List<PackagePart>();
            PackagePart mainDocumentPart = _primaryDocumentPart;

            // get custom XML parts related to the main part 
            foreach (PackageRelationship relationship in mainDocumentPart.GetRelationshipsByType(RELATIONSHIP_CONNECTIONS))
            {
                Uri documentUri = PackUriHelper.ResolvePartUri(new Uri("/", UriKind.Relative), relationship.TargetUri);

                // Appending on the parent URI to the child URI
                string rootUriString = relationship.SourceUri.OriginalString;
                int indexSlash = rootUriString.LastIndexOf("/");
                if (indexSlash > -1)
                    rootUriString = rootUriString.Substring(0, indexSlash);

                Uri documentUriLocation = new Uri(rootUriString + documentUri.OriginalString, UriKind.Relative);
                // MainDocumentPart's URI /xl/workbook.xml
                foundCustomXmlParts.Add(_package.GetPart(documentUriLocation));
            }
            return foundCustomXmlParts;
        }

        /// <summary>
        /// Setup the namespace for the current XmlDocument
        /// </summary>
        /// <param name="doc"></param>
        /// <param name="qualifier"></param>
        /// <returns></returns>
        private XmlNamespaceManager SetupDocumentNamespaceQualifier(XmlDocument doc, string qualifier)
        {
            // Select all content controls
            NameTable nt = new NameTable();
            XmlNamespaceManager nsManager = new XmlNamespaceManager(nt);
            string documentNameSpaceURI = doc.DocumentElement.NamespaceURI;
            nsManager.AddNamespace(qualifier, documentNameSpaceURI);
            return nsManager;
        }

        /// <summary>
        /// Set the Connection stored in the excel file
        /// </summary>
        /// <param name="columnName"></param>
        /// <param name="columnNewValue"></param>
        public void SetDataConnectionProperties(string connectionName, string newOdcLocation)
        {
            // Get custom XML parts related to the main part 
            foreach (PackagePart connectionXmlDocumentPart in GetConnectionXmlPartList())
            {
                // Load Custom XML part
                XmlDocument doc = new XmlDocument();
                doc.Load(connectionXmlDocumentPart.GetStream());

                XmlNodeList xnNodeList = doc.GetElementsByTagName("connection");
                foreach (XmlNode xnNode in xnNodeList)
                {
                    XmlAttribute nameAttribute = xnNode.Attributes["name"];
                    if (nameAttribute != null)
                    {
                        if (nameAttribute.Value.ToUpper() == connectionName.ToUpper())
                        {
                            XmlAttribute urlAttribute = xnNode.Attributes["odcFile"];
                            if (urlAttribute != null)
                            {
                                urlAttribute.Value = newOdcLocation;
                                doc.Save(connectionXmlDocumentPart.GetStream(FileMode.Create, FileAccess.Write));
                                break;
                            }
                        }
                    }
                }
            }
        }


        /// <summary>
        /// Return the underlying package object
        /// </summary>
        public Package Package
        {
            get { return _package; }
        }

        /// <summary>
        /// Close the package
        /// </summary>
        public void ClosePackage()
        {
            Dispose();
        }


        #region IDisposable Members

        /// <summary>
        /// Close the package
        /// </summary>
        public void Dispose()
        {
            _package.Close();
        }

        #endregion
    }
}
